

package cn.iocoder.yudao.module.apijson.framework.service;

import apijson.*;
import apijson.orm.AbstractFunctionParser;
import apijson.orm.script.JavaScriptExecutor;
import apijson.orm.script.ScriptExecutor;
import cn.iocoder.yudao.module.apijson.framework.*;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import java.rmi.ServerException;
import java.util.*;

import static apijson.JSON.*;
import static apijson.JSONRequest.KEY_COUNT;
import static apijson.RequestMethod.*;
import static cn.iocoder.yudao.module.apijson.framework.APIJSONConstant.FUNCTION_;
import static cn.iocoder.yudao.module.apijson.framework.APIJSONConstant.SCRIPT_;


/**
 * 可远程调用的函数类
 *
 * @author Lemon
 */
public class APIJSONFunctionParserBase extends AbstractFunctionParser<Long, JSONObject, JSONArray> {
    public static final String TAG = "APIJSONFunctionParser";

    @NotNull
    public static final String[] ALL_METHODS;

    static {
        ALL_METHODS = new String[]{GET.name(), HEAD.name(), GETS.name(), HEADS.name(), POST.name(), PUT.name(), DELETE.name()};
    }

    public APIJSONFunctionParserBase() {
        this(null, null, 0, null);
    }

    public APIJSONFunctionParserBase(RequestMethod method, String tag, int version, JSONObject curObj) {
        super(method, tag, version, curObj);
    }

    @Override
    public APIJSONFunctionParserBase setMethod(RequestMethod method) {
        super.setMethod(method);
        return this;
    }

    @Override
    public APIJSONFunctionParserBase setTag(String tag) {
        super.setTag(tag);
        return this;
    }

    @Override
    public APIJSONFunctionParserBase setVersion(int version) {
        super.setVersion(version);
        return this;
    }

    /**
     * 初始化，加载所有远程函数配置，并校验是否已在应用层代码实现
     */
    public static JSONObject init() throws ServerException {
        return init(false);
    }

    /**
     * 初始化，加载所有远程函数配置，并校验是否已在应用层代码实现
     */
    public static JSONObject init(boolean shutdownWhenServerError) throws ServerException {
        return init(shutdownWhenServerError, null);
    }

    /**
     * 初始化，加载所有远程函数配置，并校验是否已在应用层代码实现
     */
    public static JSONObject init(APIJSONCreator creator) throws ServerException {
        return init(false, creator);
    }

    /**
     * 初始化，加载所有远程函数配置，并校验是否已在应用层代码实现
     */
    public static JSONObject init(boolean shutdownWhenServerError, APIJSONCreator creator) throws ServerException {
        return init(shutdownWhenServerError, creator, null);
    }

    /**
     * 初始化，加载所有远程函数配置，并校验是否已在应用层代码实现
     *
     * @param table 表内自定义数据过滤条件
     */
    @SuppressWarnings("unchecked")
    public static JSONObject init(boolean shutdownWhenServerError
            , APIJSONCreator creator, JSONObject table) throws ServerException {
        if (creator == null) {
            creator = APIJSONApplication.DEFAULT_APIJSON_CREATOR;
        }

        boolean isAll = table == null || table.isEmpty();

        //JSONRequest function = isAll ? JSON.createJSONObject() : table;
        //if (Log.DEBUG == false) {
        //	function.put(APIJSONConstant.KEY_DEBUG, 0);
        //}
        //
        //JSONRequest functionItem = JSON.createJSONObject();
        //functionItem.put(FUNCTION_, function);
        //
        //JSONRequest script = JSON.createJSONObject(); // isAll ? JSON.createJSONObject() : table;
        //script.put("simple", 0);
        //if (Log.DEBUG == false) {
        //    script.put(APIJSONConstant.KEY_DEBUG, 0);
        //}
        // 不能用这个来优化，因为可能配置了不校验远程函数是否存在
        //{   // name{}@ <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        //JSONRequest nameInAt = JSON.createJSONObject();
        //nameInAt.put("from", "Function");
        //{   // Function <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        //    JSONRequest fun = JSON.createJSONObject();
        //    fun.setColumn("name");
        //    nameInAt.put("Function", fun);
        //}   // Function >>>>>>>>>>>>>>>>>>>>>>>>>>>>>

        //script.put("name{}@", nameInAt);
        //}   // name{}@ >>>>>>>>>>>>>>>>>>>>>>>>>>>>>

        //JSONRequest scriptItem = JSON.createJSONObject();
        //scriptItem.put(SCRIPT_, script);

        JSONObject request = JSON.createJSONObject();
        //request.putAll(functionItem.toArray(0, 0, FUNCTION_));
        //request.putAll(scriptItem.toArray(0, 0, SCRIPT_));

        // 可以用它，因为 Function 表必须存在，没有绕过校验的配置 // 不能用这个来优化，因为可能配置了不校验远程函数是否存在
        {   // [] <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            JSONObject item = JSON.createJSONObject();

            {   // Function <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                JSONObject function = isAll ? JSON.createJSONObject() : table;
                if (!Log.DEBUG) {
                    function.put(APIJSONConstant.KEY_DEBUG, 0);
                }
                item.put(FUNCTION_, function);
            }   // Function >>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            if (ENABLE_SCRIPT_FUNCTION) { // Script <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                JSONObject script = JSON.createJSONObject();
                script.put("name@", "/Function/name");
                script.put("simple", 0);
                item.put(SCRIPT_, script);
            }   // Script >>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            item.put(KEY_COUNT, 0);
            request.put("[]", item);
        }   // [] >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


        JSONObject response = creator.createParser().setMethod(GET).setNeedVerify(false).parseResponse(request);
        if (!JSONResponse.isSuccess(response)) {
            onServerError("\n\n\n\n\n !!!! 查询远程函数异常 !!!\n" + response.get(JSONResponse.KEY_MSG) + "\n\n\n\n\n", shutdownWhenServerError);
        }

        //初始化默认脚本引擎,避免增量
        if (isAll || SCRIPT_EXECUTOR_MAP.get("js") == null) {
            ScriptExecutor javaScriptExecutor = new JavaScriptExecutor<>();
            javaScriptExecutor.init();
            SCRIPT_EXECUTOR_MAP.put("js", javaScriptExecutor);
            SCRIPT_EXECUTOR_MAP.put("JavaScript", javaScriptExecutor);
            SCRIPT_EXECUTOR_MAP.put("javascript", javaScriptExecutor);
        }

        Map<String, JSONObject> scriptMap = new HashMap<>();
        JSONArray scriptList = JSON.get(response, "[]"); // response.getJSONArray(SCRIPT_ + "[]");
        if (scriptList != null && !scriptList.isEmpty()) {
            //if (isAll) {
            //    SCRIPT_MAP = new LinkedHashMap<>();
            //}
            Map<String, JSONObject> newMap = new LinkedHashMap<>();

            for (int i = 0; i < scriptList.size(); i++) {
                JSONObject item = JSON.get(scriptList, i);
                item = item == null ? null : JSON.get(item, SCRIPT_);
                if (item == null) { // 关联查不到很正常
                    continue;
                }

                String n = getString(item, "name");
                if (!StringUtil.isName(n)) {
                    onServerError("Script 表字段 name 的值 " + n + " 不合法！必须为合法的方法名字符串！", shutdownWhenServerError);
                }

                String s = getString(item, "script");
                if (StringUtil.isEmpty(s, true)) {
                    onServerError("Script 表字段 script 的值 " + s + " 不合法！不能为空！", shutdownWhenServerError);
                }
                newMap.put(n, item);
            }

            scriptMap = newMap;
        }

        // response.getJSONArray(FUNCTION_ + "[]");
        int size = scriptList == null ? 0 : scriptList.size();
        if (isAll && size <= 0) {
            Log.w(TAG, "init isAll && size <= 0，，没有可用的远程函数");
            return response;
        }


        if (isAll) {  // 必须在测试 invoke 前把配置 put 进 FUNCTION_MAP！ 如果要做成完全校验通过才更新 FUNCTION_MAP，但又不提供 忽略校验 参数，似乎无解
            FUNCTION_MAP = new LinkedHashMap<>();
        }
        Map<String, Map<String, Object>> newMap = FUNCTION_MAP;  // 必须在测试 invoke 前把配置 put 进 FUNCTION_MAP！ new LinkedHashMap<>();

        for (int i = 0; i < size; i++) {
            JSONObject item = JSON.get(scriptList, i);
            item = item == null ? null : JSON.get(item, FUNCTION_);
            if (item == null) {
                continue;
            }

            JSONObject demo = JSON.parseObject(getString(item, "demo"));
            if (demo == null) {
                try {
                    onServerError("字段 demo 的值必须为合法且非 null 的 JSONObject 字符串！", shutdownWhenServerError);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
            String name = getString(item, "name");
            //			demo.put(apijson.JSONRequest.KEY_TAG, getString(item, apijson.JSONRequest.KEY_TAG));
            //			demo.put(apijson.JSONRequest.KEY_VERSION, item.getInteger(apijson.JSONRequest.KEY_VERSION));
            //加载脚本
            if (item.get("language") != null) {
                String language = getString(item, "language");
                // if (SCRIPT_EXECUTOR_MAP.get(language) == null) {
                // 	onServerError("找不到脚本语言 " + language + " 对应的执行引擎！请先依赖相关库并在后端 APIJSONFunctionParser<T, M, L> 中注册！", shutdownWhenServerError);
                // }
                //脚本语言执行
                if (SCRIPT_EXECUTOR_MAP.containsKey(language)) {
                    ScriptExecutor scriptExecutor = SCRIPT_EXECUTOR_MAP.get(language);
                    JSONObject script = scriptMap.get(name);
                    scriptExecutor.load(name, getString(script, "script"));
                }
            }
            newMap.put(name, item);  // 必须在测试 invoke 前把配置 put 进 FUNCTION_MAP！

            String[] methods = StringUtil.split(getString(item, "methods"));

            if (methods == null || methods.length <= 0) {
                methods = ALL_METHODS;
            }

            if (demo != null) {
                if (!demo.containsKey("result()")) {
                    demo.put("result()", getFunctionCall(name, getString(item, "arguments")));
                }
                demo.put(JSONRequest.KEY_TAG, item.get(JSONRequest.KEY_TAG));
                demo.put(JSONRequest.KEY_VERSION, item.get(JSONRequest.KEY_VERSION));
            }

            for (String method : methods) {
                APIJSONParser parser = APIJSONApplication.createParser();
                JSONObject r = parser.setMethod(RequestMethod.valueOf(method))
                        .setNeedVerify(false)
                        .parseResponse(demo);

                if (!JSONResponse.isSuccess(r)) {
                    try {
                        onServerError(JSONResponse.getMsg(r), shutdownWhenServerError);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
        return response;
    }


    protected static void onServerError(String msg, boolean shutdown) throws ServerException {
        Log.e(TAG, "\n远程函数文档测试未通过！\n请新增 demo 里的函数，或修改 Function 表里的 demo 为已有的函数示例！\n保证前端看到的远程函数文档是正确的！！！\n\n原因：\n" + msg);

        if (shutdown) {
            System.exit(1);
        } else {
            throw new ServerException(msg);
        }
    }


    public static void test() throws Exception {
        test(null);
    }

    public static void test(
            APIJSONFunctionParserBase functionParser) throws Exception {
        int i0 = 1, i1 = -2;
        JSONObject request = JSON.createJSONObject();
        request.put("id", 10);
        request.put("i0", i0);
        request.put("i1", i1);
        JSONArray arr = JSON.createJSONArray();
        arr.add(JSON.createJSONObject());
        request.put("arr", arr);

        JSONArray array = JSON.createJSONArray();
        array.add(1);//JSON.createJSONObject());
        array.add(2);//JSON.createJSONObject());
        array.add(4);//JSON.createJSONObject());
        array.add(10);//JSON.createJSONObject());
        request.put("array", array);

        request.put("position", 1);
        request.put("@position", 0);

        request.put("key", "key");
        JSONObject object = JSON.createJSONObject();
        object.put("key", "success");
        request.put("object", object);

        APIJSONParser parser = APIJSONApplication.createParser();
        parser.setRequest(request);
        if (functionParser == null) {
            functionParser = APIJSONApplication.createFunctionParser();
            functionParser.setParser(parser);
            functionParser.setMethod(parser.getMethod());
            functionParser.setTag(parser.getTag());
            functionParser.setVersion(parser.getVersion());
            functionParser.setRequest(parser.getRequest());

            //if (functionParser instanceof APIJSONFunctionParser) {
            //}
        }

        // functionParser.setKey(null);
        // functionParser.setParentPath(null);
        // functionParser.setCurrentName(null);
        functionParser.setCurrentObject(request);

        // 等数据库 Function 表加上 plus 配置再过两个以上迭代(应该是到 5.0)后再取消注释
        //		Log.i(TAG, "plus(1,-2) = " + function.invoke("plus(i0,i1)", request));
        //		AssertUtil.assertEqual(-1, function.invoke("plus(i0,i1)", request));

        Log.i(TAG, "count([1,2,4,10]) = " + functionParser.invoke("countArray(array)", request));
        AssertUtil.assertEqual(4, functionParser.invoke("countArray(array)", request));

        Log.i(TAG, "isContain([1,2,4,10], 10) = " + functionParser.invoke("isContain(array,id)", request));
        AssertUtil.assertEqual(true, functionParser.invoke("isContain(array,id)", request));

        Log.i(TAG, "getFromArray([1,2,4,10], 0) = " + functionParser.invoke("getFromArray(array,@position)", request));
        AssertUtil.assertEqual(1, functionParser.invoke("getFromArray(array,@position)", request));

        Log.i(TAG, "getFromObject({key:\"success\"}, key) = " + functionParser.invoke("getFromObject(object,key)", request));
        AssertUtil.assertEqual("success", functionParser.invoke("getFromObject(object,key)", request));

    }


    /**
     * 获取远程函数的demo，如果没有就自动补全
     */
    public JSONObject getFunctionDemo(@NotNull JSONObject curObj) {
        JSONObject demo = JSON.parseObject(getString(curObj, "demo"));
        if (demo == null) {
            demo = JSON.createJSONObject();
        }
        if (!demo.containsKey("result()")) {
            demo.put("result()", getFunctionCall(getString(curObj, "name"), getString(curObj, "arguments")));
        }
        return demo;
    }

    /**
     * 获取远程函数的demo，如果没有就自动补全
     */
    public String getFunctionDetail(@NotNull JSONObject curObj) {
        return getFunctionCall(getString(curObj, "name"), getString(curObj, "arguments"))
                + ": " + StringUtil.trim(getString(curObj, "detail"));
    }

    /**
     * 获取函数调用代码
     */
    private static String getFunctionCall(String name, String arguments) {
        return name + "(" + StringUtil.trim(arguments) + ")";
    }


    public double plus(@NotNull JSONObject curObj, String i0, String i1) {
        return getDoubleValue(curObj, i0) + getDoubleValue(curObj, i1);
    }

    public double minus(@NotNull JSONObject curObj, String i0, String i1) {
        return getDoubleValue(curObj, i0) - getDoubleValue(curObj, i1);
    }

    public double multiply(@NotNull JSONObject curObj, String i0, String i1) {
        return getDoubleValue(curObj, i0) * getDoubleValue(curObj, i1);
    }

    public double divide(@NotNull JSONObject curObj, String i0, String i1) {
        return getDoubleValue(curObj, i0) / getDoubleValue(curObj, i1);
    }

    public double plus(@NotNull JSONObject curObj, Number n0, Number n1) {
        return n0.doubleValue() + n1.doubleValue();
    }

    public double minus(@NotNull JSONObject curObj, Number n0, Number n1) {
        return n0.doubleValue() - n1.doubleValue();
    }

    public double multiply(@NotNull JSONObject curObj, Number n0, Number n1) {
        return n0.doubleValue() * n1.doubleValue();
    }

    public double divide(@NotNull JSONObject curObj, Number n0, Number n1) {
        return n0.doubleValue() / n1.doubleValue();
    }

    //判断是否为空 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<

    /**
     * 判断array是否为空
     */
    public boolean isArrayEmpty(@NotNull JSONObject curObj, String array) {
        return BaseModel.isEmpty((Collection<?>) getJSONArray(curObj, array));
    }

    /**
     * 判断object是否为空
     */
    public boolean isObjectEmpty(@NotNull JSONObject curObj, String object) {
        return BaseModel.isEmpty((Map<?, ?>) getJSONObject(curObj, object));
    }
    //判断是否为空 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    //判断是否为包含 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<

    /**
     * 判断array是否包含value
     */
    public boolean isContain(@NotNull JSONObject curObj, String array, String value) {
        //解决isContain((List<Long>) [82001,...], (Integer) 82001) == false及类似问题, list元素可能是从数据库查到的bigint类型的值
        //		return BaseModel.isContain(getJSONArray(curObj, array), curObj.get(value));

        //不用准确的的 getString(curObj, value).getClass() ，因为Long值转Integer崩溃，而且转成一种类型本身就和字符串对比效果一样了。
        List<String> list = JSON.parseArray(getString(curObj, array), String.class);
        return list != null && list.contains(getString(curObj, value));
    }

    /**
     * 判断object是否包含key
     */
    public boolean isContainKey(@NotNull JSONObject curObj, String object, String key) {
        return BaseModel.isContainKey(getJSONObject(curObj, object), getString(curObj, key));
    }

    /**
     * 判断object是否包含value
     */
    public boolean isContainValue(@NotNull JSONObject curObj, String object, String value) {
        return BaseModel.isContainValue(getJSONObject(curObj, object), curObj.get(value));
    }
    //判断是否为包含 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>


    //获取集合长度 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<

    /**
     * 获取数量
     */
    public int countArray(@NotNull JSONObject curObj, String array) {
        return BaseModel.count((Collection<?>) getJSONArray(curObj, array));
    }

    /**
     * 获取数量
     */
    public int countObject(@NotNull JSONObject curObj, String object) {
        return BaseModel.count((Map<?, ?>) getJSONObject(curObj, object));
    }
    //获取集合长度 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>


    //根据键获取值 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<

    /**
     * 获取
     *
     * @param position 支持直接传数字，例如 getFromArray(array,0) ；或者引用当前对象的值，例如 "@position": 0, "result()": "getFromArray(array,@position)"
     */
    public Object getFromArray(@NotNull JSONObject curObj, String array, String position) {
        int p;
        try {
            p = Integer.parseInt(position);
        } catch (Exception e) {
            p = getIntValue(curObj, position);
        }
        return BaseModel.get(getJSONArray(curObj, array), p);
    }

    /**
     * 获取
     */
    public Object getFromObject(@NotNull JSONObject curObj, String object, String key) {
        return BaseModel.get(getJSONObject(curObj, object), getString(curObj, key));
    }
    //根据键获取值 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    //根据键移除值 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<

    /**
     * 移除
     *
     * @param position 支持直接传数字，例如 getFromArray(array,0) ；或者引用当前对象的值，例如 "@position": 0, "result()": "getFromArray(array,@position)"
     */
    public Object removeIndex(@NotNull JSONObject curObj, String position) {
        int p;
        try {
            p = Integer.parseInt(position);
        } catch (Exception e) {
            p = getIntValue(curObj, position);
        }
        curObj.remove(p);
        return null;
    }

    /**
     * 移除
     */
    public Object removeKey(@NotNull JSONObject curObj, String key) {
        curObj.remove(key);
        return null;
    }
    //根据键获取值 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>


    //获取非基本类型对应基本类型的非空值 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<

    /**
     * 获取非空值
     */
    public boolean booleanValue(@NotNull JSONObject curObj, String value) {
        return getBooleanValue(curObj, value);
    }

    /**
     * 获取非空值
     */
    public int intValue(@NotNull JSONObject curObj, String value) {
        return getIntValue(curObj, value);
    }

    /**
     * 获取非空值
     */
    public long longValue(@NotNull JSONObject curObj, String value) {
        return getLongValue(curObj, value);
    }

    /**
     * 获取非空值
     */
    public float floatValue(@NotNull JSONObject curObj, String value) {
        return getFloatValue(curObj, value);
    }

    /**
     * 获取非空值
     */
    public double doubleValue(@NotNull JSONObject curObj, String value) {
        return getDoubleValue(curObj, value);
    }
    //获取非基本类型对应基本类型的非空值 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    /**
     * 获取value，当value为null时获取defaultValue
     *
     * @return v == null ? curObj.get(defaultValue) : v
     */
    public Object getWithDefault(@NotNull JSONObject curObj, String value, String defaultValue) {
        Object v = curObj.get(value);
        return v == null ? curObj.get(defaultValue) : v;
    }

}
